/*
 * Created by Gibbs on 2021/1/1.
 * Copyright (c) 2021 Gibbs. All rights reserved.
 */

#ifndef GPLAYER_GPLAYER_H
#define GPLAYER_GPLAYER_H

#include <android/native_window.h>
#include "DecoderHelper.h"
#include "DemuxerHelper.h"
#include "MessageHelper.h"
#include "GPlayerJni.h"
#include "LoopThread.h"
#include "PacketSource.h"
#include "FrameSource.h"
#include "MessageSource.h"
#include "RenderHelper.h"

#define AV_FLAG_SOURCE_MEDIA_CODEC 0x00000002

#define THREAD_NAME_DEMUXING         "demuxingThread"
#define THREAD_NAME_AUDIO_DECODING   "audioDecodingThread"
#define THREAD_NAME_VIDEO_DECODING   "videoDecodingThread"
#define THREAD_NAME_AUDIO_RENDERING  "audioRenderingThread"
#define THREAD_NAME_VIDEO_RENDERING  "videoRenderingThread"
#define THREAD_NAME_MESSAGING        "messagingThread"

class GPlayer {

public:
    GPlayer(uint32_t flag, jobject obj);

    ~GPlayer();

public:
    void setSurface(ANativeWindow *window);

    void setAudioTrack(AudioTrackJni *track);

    void prepare(const std::string &url);

    void start();

    void pause();

    void resume();

    void seekTo(uint32_t secondUs);

    void stop();

    void setFlags(uint32_t flags) {
        mFlags = flags;
    }

    int getDuration();

    int getVideoWidth();

    int getVideoHeight();

    void onThreadStateChanged(int state, const std::string &threadName);

    void onSeekStateChanged(int state);

    void onBufferStateChanged(int audioSize, int videoSize);

    void onPlayStateChanged(int state, long extra);

    void onPlayStateChanged(int state, long extra, bool force);

private:
    int processMessage(int arg1, long arg2);

    void startMessageLoop();

    void stopMessageLoop();

    void startDemuxing(const std::string &url);

    void stopDemuxing();

    void startDecoding();

    void stopDecoding();

    void startRendering();

    void stopRendering();

    void pauseThreads(bool demuxing, bool decoding, bool rendering, bool messaging);

    void resumeThreads(bool demuxing, bool decoding, bool rendering, bool messaging);

    void waitThread() {
        std::unique_lock<std::mutex> lck(threadConLock);
        threadConVar.wait_for(lck, std::chrono::milliseconds(5000));
    }

    void notifyThread() {
        threadConVar.notify_all();
    }

private:
    uint32_t mFlags;
    ANativeWindow *nativeWindow = nullptr;
    AudioTrackJni *audioTrackJni = nullptr;

    MessageSource *messageSource;
    PacketSource *packetSource;
    FrameSource *frameSource;

    DemuxerHelper *demuxerHelper;
    DecoderHelper *decoderHelper;
    RenderHelper  *renderHelper;
    MessageHelper *messageHelper;

    LoopThread *demuxerThread;
    LoopThread *audioDecodeThread;
    LoopThread *videoDecodeThread;
    LoopThread *audioRenderThread;
    LoopThread *videoRenderThread;
    LoopThread *messageThread;

    int playState = -1;
    int bufferState = -1;
    int seekState = -1;

    std::mutex playerLock;
    std::mutex threadConLock;
    std::condition_variable threadConVar;
};

static std::string getPlayStateString(int state) {
    switch (state) {
        case STATE_IDLE:
            return "idle";
        case STATE_INITIALIZED:
            return "initialized";
        case STATE_PREPARING:
            return "preparing";
        case STATE_PREPARED:
            return "prepared";
        case STATE_STARTED:
            return "started";
        case STATE_PAUSED:
            return "pause";
        case STATE_STOPPED:
            return "stopped";
        case STATE_END:
            return "end";
        case STATE_COMPLETED:
            return "completed";
        case STATE_ERROR:
            return "error";
        default:
            return "invalid";
    }
}

#endif //GPLAYER_GPLAYER_H
